<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>手写防抖与节流</title>
    <style>
    body {
        font-family: 'Arial', sans-serif;
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
        line-height: 1.6;
    }

    .container {
        display: flex;
        gap: 20px;

    }

    .section {
        border: 1px solid #ddd;
        border-radius: 8px;
        padding: 15px;
        margin-bottom: 10px;
    }

    .logcont {
        flex: 0.4;
    }

    .contentcont {
        flex: 0.6;
    }

    h2 {
        color: #333;
        border-bottom: 2px solid #eee;
        padding-bottom: 10px;
    }

    button {
        padding: 10px 15px;
        font-size: 16px;
        cursor: pointer;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 4px;
        transition: background-color 0.3s;
        margin: 10px;
    }

    button:hover {
        background-color: #45a049;
    }

    .result {
        margin-top: 10px;
        padding: 10px;
        background-color: #f9f9f9;
        border-radius: 4px;
        min-height: 20px;
    }

    .log {
        font-family: monospace;
        font-size: 14px;
        color: #555;
    }
    .logarea{
        white-space: pre-wrap;
    }
    </style>
</head>

<body>
    <h1>防抖与节流示例</h1>
    <div class="container">
        <div class="logcont">
            <!-- 日志区域 -->
            <div class="section">
                <h2>执行日志</h2>
                <div class="log" id="logArea"></div>
            </div>
        </div>
        <div class="contentcont">
            <!-- 防抖示例 -->
            <div class="section">
                <h2>防抖 (Debounce)</h2>
                <p>防抖: 在事件被触发后等待一段时间，如果在这段时间内没有再次触发事件，才执行回调函数。如果在这段时间内再次触发了事件，则重新计时。</p>
                <button id="debounceBtn">防抖按钮 (快速点击)</button>
                <button id="cancelBtn">取消执行</button>
                <button id="immediateBtn">立即执行</button>
                <button id="statusBtn">获取状态</button>
                <button id="tioashiBtn">调试信息</button>
                <div class="result" id="debounceResult"></div>
            </div>
            <!-- 节流示例 -->
            <div class="section">
                <h2>节流 (Throttle)</h2>
                <p>节流: 规定一个时间单位，在这个时间单位内，事件处理函数只能执行一次。如果在这个时间单位内多次触发事件，只有第一次会执行。</p>
                <button id="throttleBtn">节流按钮 (快速点击)</button>
                <div class="result" id="throttleResult"></div>
            </div>
        </div>
    </div>
    <script>
    // 防抖函数--基础
    function debounce(func, wait) {
        let timeout;
        return function() {
            const context = this;
            const args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(function() {
                func.apply(context, args);
            }, wait);
        };
    }

    // 防抖函数-优化1-箭头函数
    // const debounce = (func, wait) => {
    //     let timeout;
    //     return (...args) => {
    //         clearTimeout(timeout);
    //         timeout = setTimeout(() => func(...args), wait);
    //     };
    // };

    // 防抖函数-优化2-取消执行
    // const debounce = (func, wait) => {
    //     let timeout;
    //     const debounced = (...args) => {
    //         clearTimeout(timeout);
    //         timeout = setTimeout(() => func(...args), wait);
    //     };
    //     debounced.cancel = () => {
    //         clearTimeout(timeout);
    //         timeout = null;
    //     };
    //     return debounced;
    // };


    /**
     * 创建一个防抖函数-优化3-立即执行
     * @param {Function} func - 需要防抖的函数
     * @param {number} wait - 防抖的时间间隔（毫秒）
     * @param {boolean} immediate - 是否在首次触发时立即执行函数
     * @returns {Function} - 返回新的防抖函数，带有cancel和pending方法
     */
    // const debounce = (func, wait, immediate = false) => {
    //     // 存储setTimeout返回的ID
    //     let timeout;
    //     // 标记是否有待执行的函数
    //     let pending = false;
    //     // 创建防抖函数
    //     const debounced = (...args) => {
    //         // 定义延迟执行的函数
    //         const later = () => {
    //             // 清除timeout引用
    //             timeout = null;
    //             // 重置pending状态
    //             pending = false;
    //             // 如果不是立即执行模式，则执行原函数
    //             if (!immediate) func(...args);
    //         };
    //         // 判断是否应该立即执行
    //         // 如果是立即执行模式且当前没有待执行的函数（timeout为null）
    //         const callNow = immediate && !timeout;
    //         // 清除之前的定时器
    //         clearTimeout(timeout);
    //         // 设置新的定时器
    //         timeout = setTimeout(later, wait);
    //         // 标记有待执行的函数
    //         pending = true;
    //         // 如果应该立即执行，则调用函数
    //         if (callNow) func(...args);
    //     };


    //     // 添加取消方法
    //     debounced.cancel = () => {
    //         // 清除定时器
    //         clearTimeout(timeout);
    //         // 清除timeout引用
    //         timeout = null;
    //         // 重置pending状态
    //         pending = false;
    //     };
    //     // 添加检查是否有待执行函数的方法
    //     debounced.pending = () => pending;
    //     // 返回防抖函数
    //     return debounced;
    // };


    // // 创建一个防抖函数-优化4-记录状态
    // const debounce = (func, wait, immediate = false) => {
    //     // 存储setTimeout返回的ID
    //     let timeout;
    //     // 标记是否有待执行的函数
    //     let pending = false;
    //     // 存储上一次执行的时间戳
    //     let lastExecTime = 0;
    //     // 存储函数执行次数
    //     let executionCount = 0;

    //     // 创建防抖函数
    //     const debounced = (...args) => {
    //         // 记录当前时间
    //         const now = Date.now();

    //         // 定义延迟执行的函数
    //         const later = () => {
    //             // 清除timeout引用
    //             timeout = null;
    //             // 重置pending状态
    //             pending = false;
    //             // 更新执行时间
    //             lastExecTime = Date.now();
    //             // 增加执行计数
    //             executionCount++;
    //             // 如果不是立即执行模式，则执行原函数
    //             if (!immediate) func(...args);
    //         };

    //         // 判断是否应该立即执行
    //         // 如果是立即执行模式且当前没有待执行的函数（timeout为null）
    //         const callNow = immediate && !timeout;

    //         // 清除之前的定时器
    //         clearTimeout(timeout);
    //         // 设置新的定时器
    //         timeout = setTimeout(later, wait);
    //         // 标记有待执行的函数
    //         pending = true;

    //         // 如果应该立即执行，则调用函数
    //         if (callNow) {
    //             func(...args);
    //             // 更新执行时间
    //             lastExecTime = Date.now();
    //             // 增加执行计数
    //             executionCount++;
    //         }
    //     };

    //     // 添加取消方法
    //     debounced.cancel = () => {
    //         // 清除定时器
    //         clearTimeout(timeout);
    //         // 清除timeout引用
    //         timeout = null;
    //         // 重置pending状态
    //         pending = false;
    //     };

    //     // 检查是否有待执行函数的方法
    //     debounced.pending = () => pending;

    //     // 添加获取状态的方法
    //     debounced.getStatus = () => ({
    //         pending, // 是否有待执行的函数
    //         lastExecTime, // 上一次执行的时间戳
    //         executionCount, // 执行次数
    //         wait, // 防抖时间间隔
    //         immediate, // 是否为立即执行模式
    //         timeSinceLastExec: lastExecTime ? Date.now() - lastExecTime : null // 距离上次执行的时间
    //     });

    //     // 返回防抖函数
    //     return debounced;
    // };

    /**
     * 创建一个防抖函数-优化5 
     * @param {Function} func - 需要防抖的函数
     * @param {number} wait - 防抖的时间间隔（毫秒）
     * @param {Object} options - 配置选项
     * @param {boolean} options.immediate - 是否在首次触发时立即执行函数
     * @param {boolean} options.trailing - 是否在延迟结束后执行函数
     * @param {boolean} options.leading - 是否在延迟开始时执行函数
     * @returns {Function} - 返回新的防抖函数，带有多个实用方法
     */
    const debounce = (func, wait, options = {}) => {
        // 合并默认选项
        const {
            immediate = false,
                trailing = true, // 默认在延迟结束后执行
                leading = false // 默认不在延迟开始时执行
        } = options;

        // 存储setTimeout返回的ID
        let timeout;
        // 标记是否有待执行的函数
        let pending = false;
        // 存储上一次执行的时间戳
        let lastExecTime = 0;
        // 存储函数执行次数
        let executionCount = 0;
        // 存储上一次调用的参数
        let lastArgs = null;
        // 存储函数名称，用于调试
        const funcName = func.name || 'anonymous';
        // 创建唯一标识符
        const debounceId = `debounce-${funcName}-${Date.now()}`;

        // 创建防抖函数
        const debounced = (...args) => {
            // 记录当前时间
            const now = Date.now();

            // 保存参数，用于后续执行
            lastArgs = args;

            // 定义延迟执行的函数
            const later = () => {
                // 清除timeout引用
                timeout = null;
                // 重置pending状态
                pending = false;

                // 如果启用了trailing模式，执行函数
                if (trailing && !immediate) {
                    lastExecTime = Date.now();
                    executionCount++;
                    func(...lastArgs);
                }
            };

            // 判断是否应该立即执行
            const callNow = (leading || immediate) && !timeout;

            // 如果是立即执行模式且不是trailing模式
            if (callNow) {
                func(...args);
                lastExecTime = Date.now();
                executionCount++;
            }

            // 清除之前的定时器
            clearTimeout(timeout);

            // 如果启用了trailing模式，设置新的定时器
            if (trailing) {
                timeout = setTimeout(later, wait);
                pending = true;
            }
        };

        // 添加取消方法
        debounced.cancel = () => {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
                pending = false;
                console.log(`[Debounce] ${debounceId} 已取消`);
            }
        };

        // 添加刷新方法 - 重置计时器但不取消执行
        debounced.flush = () => {
            if (timeout && pending) {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    timeout = null;
                    pending = false;
                    if (trailing && !immediate) {
                        lastExecTime = Date.now();
                        executionCount++;
                        func(...lastArgs);
                    }
                }, wait);
                console.log(`[Debounce] ${debounceId} 已刷新计时器`);
            }
        };

        // 添加立即执行方法
        debounced.run = () => {
            if (pending || !timeout) {
                func(...(lastArgs || []));
                lastExecTime = Date.now();
                executionCount++;
                console.log(`[Debounce] ${debounceId} 立即执行`);
            }
        };

        // 检查是否有待执行函数的方法
        debounced.pending = () => pending;

        // 添加获取状态的方法
        debounced.getStatus = () => ({
            id: debounceId, // 唯一标识符
            pending, // 是否有待执行的函数
            lastExecTime, // 上一次执行的时间戳
            executionCount, // 执行次数
            wait, // 防抖时间间隔
            immediate, // 是否为立即执行模式
            trailing, // 是否在延迟结束后执行
            leading, // 是否在延迟开始时执行
            timeSinceLastExec: lastExecTime ? Date.now() - lastExecTime : null, // 距离上次执行的时间
            lastArgs: lastArgs ? `[${lastArgs.join(', ')}]` : null // 上一次调用的参数
        });

        // 添加重置方法 - 重置所有状态
        debounced.reset = () => {
            debounced.cancel();
            lastExecTime = 0;
            executionCount = 0;
            lastArgs = null;
            console.log(`[Debounce] ${debounceId} 已重置`);
        };

        // 添加调试日志方法
        debounced.debug = (message) => {
            console.log(`[Debounce] ${debounceId}: ${message}`, debounced.getStatus());
        };

        // 添加函数标记，便于调试
        debounced.displayName = `debounced(${funcName})`;

        return debounced;
    };


    // 节流函数
    // function throttle(func, limit) {
    //     let inThrottle;
    //     return function() {
    //         const context = this;
    //         const args = arguments;
    //         if (!inThrottle) {
    //             func.apply(context, args);
    //             inThrottle = true;
    //             setTimeout(() => inThrottle = false, limit);
    //         }
    //     };
    // }

    // 节流函数-优化写法1 
    const throttle=(func,limit)=>{
        let inThrottle;
        return (...args)=>{
            if(!inThrottle){
                func(...args);
                inThrottle=true;
                setTimeout(()=>{inThrottle=false},limit)
            }
        }
    }




    // 日志函数
    function log(message) {
        const logArea = document.getElementById('logArea');
        const timestamp = new Date().toLocaleTimeString();
        logArea.innerHTML += `<div class="logarea">${timestamp}: ${message}</div>`;
    }

    // 防抖按钮点击处理
    const debounceHandler = function() {
        const result = document.getElementById('debounceResult');
        const count = parseInt(result.getAttribute('data-count') || 0) + 1;
        result.setAttribute('data-count', count);
        result.innerHTML = `防抖函数执行次数: ${count}`;
        log(`防抖函数执行 - 第${count}次`);
    };

    // 节流按钮点击处理
    const throttleHandler = function() {
        const result = document.getElementById('throttleResult');
        const count = parseInt(result.getAttribute('data-count') || 0) + 1;
        result.setAttribute('data-count', count);
        result.innerHTML = `节流函数执行次数: ${count}`;
        log(`节流函数执行 - 第${count}次`);
    };

    // 应用防抖和节流
    document.getElementById('debounceBtn').addEventListener('click', debounce(debounceHandler, 2000));
    document.getElementById('throttleBtn').addEventListener('click', throttle(throttleHandler, 2000));

    // 取消执行
    document.getElementById('cancelBtn').addEventListener('click', () => {
        debounce(debounceHandler, 2000).cancel();
        const result = document.getElementById('debounceResult');
        result.innerHTML = `防抖函数已取消执行`;
        log(`防抖函数被取消执行`);
    });
    // 立即执行
    document.getElementById('immediateBtn').addEventListener('click', () => {
        debounce(() => console.log('立即执行'), 2000, true)
        const result = document.getElementById('debounceResult');
        result.innerHTML = `防抖函数立即执行`;
        log(`防抖函数立即执行`);
    });

    // 获取状态
    document.getElementById('statusBtn').addEventListener('click', () => {
        const debouncedFn = debounce(() => console.log('执行'), 2000, true);
        const result = document.getElementById('debounceResult');
        const resulttxt = debouncedFn.getStatus();
        console.log(resulttxt);
        result.innerHTML = `防抖函数状态`;
        log(`
        防抖函数立即执行
        pending:${resulttxt.pending},//是否有待执行的函数
        lastExecTime:${resulttxt.lastExecTime},//上一次执行的时间戳
        executionCount:${resulttxt.executionCount},// 执行次数
        wait:${resulttxt.wait},//防抖时间间隔
        immediate:${resulttxt.immediate},  //是否为立即执行模式
        timeSinceLastExec:${resulttxt.timeSinceLastExec} //距离上次执行的时间
        `);
    });


     // 调试
    document.getElementById('tioashiBtn').addEventListener('click', () => {
        const debouncedFn = debounce((text) => {
            console.log(`处理文本: ${text}`);
        }, 2000, {
            immediate: true,
            trailing: true,
            leading: false
        });
        // 调试
        debouncedFn.debug('调试信息');
        const result = document.getElementById('debounceResult');
        result.innerHTML = `防抖函数调试`;
        log(`防抖函数调试`);
    });
    </script>
</body>

</html>